﻿
namespace Patterns4Net.PatternEnforcer.Core.Config
{
    using System;
    using System.Reflection;
    using System.Collections.Generic;
    using System.ComponentModel.Composition.Hosting;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;

    /// <summary>
    /// Provides methods for loading xml configuration of Pattern Enforcer.
    /// </summary>
    public partial class Config
    {
        public static Config Create(string filename)
        {
            if (string.IsNullOrWhiteSpace(filename))
            {
                return CreateDefault();
            }

            using (var reader = new XmlTextReader(new StreamReader(filename)))
            {
                return Create(reader);
            }
        }

        public static Config Create(XmlReader xmlReader)
        {
            var serializer = new XmlSerializer(typeof(Config));
            var result = serializer.Deserialize(xmlReader);
            return (Config)result;
        }

        public static Config CreateDefault()
        {
            return new Config { plugins = new Assembly[0] };
        }

        public static Config Create(string filename, StringBuilder errors)
        {
            if (string.IsNullOrWhiteSpace(filename))
            {
                return Config.CreateDefault();
            }

            try
            {
                return Config.Create(filename);
            }
            catch (FileNotFoundException)
            {
                errors.AppendFormat(
                    "Configuration file {0} was not found. I am using the default configuration.",
                    filename);
            }
            catch (XmlException ex)
            {
                errors.AppendFormat(
                    "Configuration file {0} does not contain valid xml. I am using the default configuration. Erro message: {1}",
                    filename,
                    ex.Message);
            }
            catch (Exception ex)
            {
                errors.AppendFormat(
                    "Configuration file {0} cannot be parsed. Unexpected exception has occurred. " +
                    "I am using the default configuration. Erro message: {1}",
                    filename,
                    ex.Message);
            }

            return CreateDefault();
        }

        public IList<AssemblyCatalog> GetPluginAssemblies(StringBuilder errors)
        {
            if (this.plugins == null)
            {
                return new List<AssemblyCatalog>();
            }

            var attributesAssembly = Path.Combine(
                Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
                "Patterns4Net.Attributes.dll");

            var pluginsCatalogs = new List<AssemblyCatalog>();
            foreach (var filenameFromConfig in this.plugins.Select(x => x.filename).Concat(new[] { attributesAssembly }))
            {
                string filename = filenameFromConfig;
                if (Path.IsPathRooted(filename) == false)
                {
                    filename = Path.Combine(Directory.GetCurrentDirectory(), filename);
                }

                try
                {
                    pluginsCatalogs.Add(
                        new AssemblyCatalog(
                            System.Reflection.Assembly.LoadFrom(filename)));
                }
                catch (FileNotFoundException ex)
                {
                    errors.AppendFormat("Plugin's assembly on path {0} was not found. Detailed message: {1}", filename, ex.Message);
                    errors.AppendLine();
                }
                catch (FileLoadException ex)
                {
                    errors.AppendFormat("Plugin's assembly on path {0} cannot be loaded. Detailed message: {1}", filename, ex.Message);
                    errors.AppendLine();
                }
                catch (BadImageFormatException ex)
                {
                    errors.AppendFormat(
                        "Plugin's assembly on path {0} cannot be loaded. " +
                        "The assembly seems to be corrupted. Try to rebuild it or " +
                        "if you use some weaving tool such as AOP framework, try to disable it. " +
                        "Detailed exception message: {1}",
                        filename,
                        ex.Message);
                    errors.AppendLine();
                }
                catch (Exception ex)
                {
                    errors.AppendFormat(
                        "Plugin's assembly on path {0} cannot be loaded. " +
                        "Unexpected exception type experienced." +
                        "Detailed exception message: {1}",
                        filename,
                        ex.Message);
                    errors.AppendLine();
                }
            }

            return pluginsCatalogs;
        }
    }
}
